/*
 * Copyright 2011 Katsunori Koyanagi
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package sweezy.swf.io
{
	import flash.utils.ByteArray;

	public class SWFOutput extends IOBase
	{

		public function SWFOutput(bytes:ByteArray)
		{
			super(bytes);
		}

		public function writeBytes(bytes:ByteArray):void
		{
			_bitCursor = 0;
			_bytes.writeBytes(bytes);
		}

		public function writeDOUBLE(value:Number):void
		{
			_bitCursor = 0;
			_bytes.writeDouble(value);
		}

		public function writeEncodedS32(value:int):void
		{
			writeEncodedU32(uint(value));
		}

		public function writeEncodedU32(value:uint):void
		{
			_bitCursor = 0;

			if (value < 128 && value > -1)
			{
				_bytes.writeByte(value);
			}
			else if (value < 16384 && value > -1)
			{
				_bytes.writeByte((value & 0x7F) | 0x80);
				_bytes.writeByte((value >> 7) & 0x7F);
			}
			else if (value < 2097152 && value > -1)
			{
				_bytes.writeByte((value & 0x7F) | 0x80);
				_bytes.writeByte((value >> 7) | 0x80);
				_bytes.writeByte((value >> 14) & 0x7F);
			}
			else if (value < 268435456 && value > -1)
			{
				_bytes.writeByte((value & 0x7F) | 0x80);
				_bytes.writeByte(value >> 7 | 0x80);
				_bytes.writeByte(value >> 14 | 0x80);
				_bytes.writeByte((value >> 21) & 0x7F);
			}
			else
			{
				_bytes.writeByte((value & 0x7F) | 0x80);
				_bytes.writeByte(value >> 7 | 0x80);
				_bytes.writeByte(value >> 14 | 0x80);
				_bytes.writeByte(value >> 21 | 0x80);
				_bytes.writeByte((value >> 28) & 0x0F);
			}
		}

		public function writeFB(length:uint, value:Number):void
		{
			writeSB(length, value * 65536);
		}

		public function writeFIXED(value:Number):void
		{
			_bitCursor = 0;
			_bytes.writeInt(int(value * 65536));
		}

		public function writeFIXED8(value:Number):void
		{
			_bitCursor = 0;
			_bytes.writeShort(int(value * 256));
		}

		public function writeFLAG(value:Boolean):void
		{
			writeUB(1, value ? 1 : 0);
		}

		public function writeFLOAT16(value:Number):void
		{
			_bitCursor = 0;

			if (value == 0)
			{
				_bytes.writeByte(0x0);
				_bytes.writeByte(0x0);
				return;
			}

			var sign:uint = 0;
			var exp:int = 16;

			if (value < 0)
			{
				sign = 0x8000;
				value *= -1;
			}
			if (value < 1)
			{
				while (uint(value) !== 1)
				{
					value *= 2;
					--exp;
				}
			}
			else
			{
				while (uint(value) !== 1)
				{
					value /= 2;
					++exp;
				}
			}

			writeUI16(sign | (exp << 10) | (uint((value - 1) * 0x400) & 0x3ff));
		}

		public function writeFLOAT32(value:Number):void
		{
			_bitCursor = 0;
			_bytes.writeFloat(value);
		}

		public function writeSB(length:uint, value:int):void
		{
			writeUB(length, value | ((value < 0 ? 0x80000000 : 0x00000000) >> (32 - length)));
		}

		public function writeSI16(value:int):void
		{
			_bitCursor = 0;
			_bytes.writeShort(value);
		}

		public function writeSI24(value:int):void
		{
			_bitCursor = 0;
			_bytes.writeByte(value & 0xff);
			_bytes.writeByte((value >> 8) & 0xff);
			_bytes.writeByte((value >> 16) & 0xff);
		}

		public function writeSI32(value:int):void
		{
			_bitCursor = 0;
			_bytes.writeInt(value);
		}

		public function writeSI64(value:Number):void
		{
			_bitCursor = 0;
			var val1:uint = value % 4294967296;
			var val2:int = value / 4294967296;
			_bytes.writeUnsignedInt(val1);
			_bytes.writeInt(val2);
		}

		public function writeSI8(value:int):void
		{
			_bitCursor = 0;
			_bytes.writeByte(value);
		}

		public function writeString(value:String, charset:String = null, nullTerminate:Boolean = false):void
		{
			if (value === null)
			{
				value = "";
			}

			_bitCursor = 0;
			if (charset !== null)
			{
				_bytes.writeMultiByte(value, charset);
			}
			else
			{
				_bytes.writeUTFBytes(value)
			}
			if (nullTerminate)
			{
				_bytes.writeByte(0x00);
			}

			return;
		}

		public function writeUB(length:uint, value:uint):void
		{
			if (length == 0)
			{
				return;
			}

			if (_bitCursor == 0)
			{
				_bytes.writeByte(0x00);
				_bitBuffer = 0;
				_bitCursor = 8;
			}

			while (true)
			{
				if (length > _bitCursor)
				{
					_bytes[_bytes.position - 1] = uint(_bitBuffer | ((value << (32 - length)) >>> (32 - _bitCursor))) & 0xff;
					length -= _bitCursor;
					_bytes.writeByte(0x00);
					_bitBuffer = 0;
					_bitCursor = 8;
				}
				else
				{
					_bytes[_bytes.position - 1] = uint(_bitBuffer |= ((value << (32 - length)) >>> (32 - _bitCursor))) & 0xff;
					_bitCursor -= length;
					break;
				}
			}
		}

		public function writeUI16(value:uint):void
		{
			_bitCursor = 0;
			_bytes.writeByte(value & 0xff);
			_bytes.writeByte((value >> 8) & 0xff);
		}

		public function writeUI32(value:uint):void
		{
			_bitCursor = 0;
			_bytes.writeByte(value & 0xff);
			_bytes.writeByte((value >> 8) & 0xff);
			_bytes.writeByte((value >> 16) & 0xff);
			_bytes.writeByte((value >> 24) & 0xff);
		}

		public function writeUI8(value:uint):void
		{
			_bitCursor = 0;
			_bytes.writeByte(value & 0xff);
		}
	}
}
